﻿#pragma once
#include <map>
#include <iostream>

//have you seen this version of program?

using namespace std;

template <typename Name>
class BiGraph {  
    public:
        class Vertex;
        class Edge; 
	    typedef map_iterator_wraper<Name, Vertex> vertex_iterator;
        typedef map_iterator_wraper<Vertex*, Edge> edge_iterator;
        typedef pair<vertex_iterator, vertex_iterator> vertices_range;
        typedef pair<edge_iterator, edge_iterator> edges_range;
    
        class Edge {
        public:
            Edge(Vertex* start, Vertex* end, double w):m_weight(w), m_start_vertex(start), m_end_vertex(end) {}
            Vertex* get_start () { return m_start_vertex; }
            Vertex* get_end () { return m_end_vertex; }
			
        private:
            double m_weight;
            Vertex* m_start_vertex;
            Vertex* m_end_vertex;
        };
    
        class Vertex {
        public:
            Vertex(const Name &_name): name(_name){}
            friend class BiGraph;  
            edges_range in_edges () {
                return make_pair(m_in_edges.begin(), m_in_edges.end());
            }
            edges_range out_edges () {
                return make_pair(m_out_edges.begin(), m_out_edges.end());
            }
        private:
            friend class BiGraph;
            Name m_name;
            map<Vertex*, Edge*> m_out_edges; //outcoming edges
            map<Vertex*, Edge*> m_in_edges; //incoming edges    
    };
    
        BiGraph():m_edges_count(0){}
      
        Edge* add_edge (const Name& start, const Name& end, double weight) {
        if (start == end) { return NULL; }

        Vertex* start_vertex = find_or_create_vertex (start);
        Vertex* end_vertex = find_or_create_vertex (end);

        typename map<Vertex*, Edge*>::iterator existing_edge = start_vertex->m_out_edges.find (end_vertex);

        if (existing_edge == start_vertex->m_out_edges.end ()) {
            ++m_edges_count;
            Edge* new_edge = new Edge(start_vertex, end_vertex, weight);
            start_vertex->m_out_edges.insert (std::make_pair (end_vertex, new_edge));
            end_vertex->m_in_edges.insert (std::make_pair (start_vertex, new_edge));
            return new_edge;
        } else { 
            return existing_edge->second; 
	    }
    }
    
        bool delete_edge (const Name& start, const Name& end) {
        if (m_vertices.find (start) == m_vertices.end ()) {
            return false;
        }

        if (m_vertices.find (end) == m_vertices.end ()) {
            return false;
        }

        Vertex* start_vertex = m_vertices[start];
        Vertex* end_vertex = m_vertices[end];

        if (start_vertex->m_out_edges.find (end_vertex) == start_vertex->m_out_edges.end ()) {
            return false; 
        }

        start_vertex->m_out_edges.erase (end_vertex);
        delete end_vertex->m_in_edges[start_vertex];
        end_vertex->m_in_edges.erase (start_vertex);

        return true;
    }
    
        size_t vertices_count() const { return m_vertices.size(); } //number of vertices
    
        size_t edges_count() const { return m_edges_count; } //number of edges
    
        bool add_vertex(const Name &v){
         if (m_vertices.find(v)!=m_vertices.end()){return false;}
         m_vertices.insert(make_pair(v, new Vertex(v)));
         return true;
    } 

        //In mint condition 
        void clear (){
        for (vertex_iterator ptr_v = m_vertices.begin(); ptr_v != m_vertices.end (); ++ptr_v) {
            for (edge_iterator ptr_e = ptr_v->m_in_edges.begin(); ptr_e != ptr_v->m_in_edges.end(); ++ptr_e) {
                delete *(ptr_e);
            }

            ptr_v->m_out_edges.clear();
            ptr_v->m_in_edges.clear ();
            delete *ptr_v;
        }

        m_vertices.clear();
        m_edges_count = 0;
    }

        //I don't completely understand, why everybody wrote this, but it will not make worse
        ~BiGraph () { clear(); }

    private:
        map<Name,Vertex*> m_vertices; //mapping in which we store the vertices

        size_t m_edges_count;
		
		//laborer, who does everything for us ^_^
        Vertex* find_or_create_vertex(const Name &v){ 
        if (m_vertices.find(v)==m_vertices.end()){
            Vertex* new_vertex = new Vertex(v);
            m_vertices.insert(make_pair(v, new_vertex));
            return new_vertex;
        }
        return m_vertices[v];
    }
    
};

template <class key, class value>
class map_iterator_wraper {
    public:
        value*& operator * () { return current->second; }

        bool operator == (const map_iterator_wraper& other) const { return current == other.current; }

        bool operator == (const typename map<key, value*>::iterator& other) const { return other == current; }

        bool operator != (const map_iterator_wraper& other) const { return !operator == (other); }

        bool operator != (const typename map<key, value*>::iterator& other) const { return other != current; }

        map_iterator_wraper& operator ++ () { current++; return *this; }

        value* operator -> () { return current -> second; }

        map_iterator_wraper (const typename map<key, value*>::iterator& src) : current(src) {}

        map_iterator_wraper operator = (const typename map<key, value*>::iterator& src) { current = src; return *this; }

private:
	//iterator on the map
    typename map<key, value*>::iterator current; 
};

int main() {
    return 0;
}